package com.zlcx.tz.live.exception;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zlcx.tz.live.common.HttpHelper;
import com.zlcx.tz.live.constant.ResultStatus;
import com.zlcx.tz.live.service.impl.ApiRequestStatisticsServiceImpl;
import com.zlcx.tz.live.utils.DateUtil;
import com.zlcx.tz.live.vo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>全局异常处理</p>
 * <PRE>
 * <BR>    修改记录
 * <BR>-----------------------------------------------
 * <BR>    修改日期         修改人          修改内容
 * </PRE>
 *
 * @author zl
 * @version 1.0
 * @date Created in 2019/3/15 12:00
 * @copyright: Copyright (c) founders
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private ApiRequestStatisticsServiceImpl apiRequestStatisticsService;
    @Resource(name = "commonTaskExecutor")
    private AsyncTaskExecutor commonTaskExecutor;
    @Value("${spring.profiles.active}")
    private String profiles;
    @Autowired
    private SendExceptionUtil sendExceptionUtil;

    /**
     * 频次限制异常处理
     *
     * @param exception
     * @return
     */
    @ExceptionHandler({RequestLimitException.class})
    public ResponseEntity<Object> handleLimitException(RequestLimitException exception) {
        logger.error("【handleLimitException】：" + exception.getMessage());
        ResultVO resultVO = new ResultVO(ResultStatus.REQUEST_LIMIT.getCode());
        resultVO.setMessage(exception.getMessage());
        if ("prod".equals(profiles)) {
            resultVO.setMessage(ResultStatus.REQUEST_LIMIT.getTips());
        }
        return ResponseEntity.status(HttpStatus.OK).body(resultVO);
    }

    /**
     * 异常处理
     *
     * @param exception exception
     * @return ResponseEntity
     */
    @ExceptionHandler({CheckTargetException.class})
    public ResponseEntity<ResultVO<Object>> handleCheckException(CheckTargetException exception, HttpServletRequest request) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        log.error("【handleCheckException】：" + exception.getMessage());
        log.error("", exception);
        ResultVO resultVO = new ResultVO(ResultStatus.TARGET_ERROR.getCode(), ResultStatus.TARGET_ERROR.getTips());
        return ResponseEntity.status(HttpStatus.OK).body(resultVO);
    }

    /**
     * 参数异常处理
     *
     * @param exception
     * @return
     */
    @ExceptionHandler({CheckParamException.class})
    public ResponseEntity<Object> handleParamException(CheckParamException exception, HttpServletRequest request) {
        logger.error("【handleParamException】：" + exception.getMessage());
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        ResultVO resultVO = new ResultVO(ResultStatus.PARAMS_ERROR.getCode());
        resultVO.setMessage(exception.getMessage());
        if ("prod".equals(profiles)) {
            resultVO.setMessage(ResultStatus.PARAMS_ERROR.getTips());
        }
        return ResponseEntity.status(HttpStatus.OK).body(resultVO);
    }

    /**
     * 业务异常处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler({BusinessException.class})
    public ResponseEntity<Object> handleBusinessException(HttpServletRequest request,BusinessException e) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paramInfo = "";
        paramInfo = getParam(request, method, paramInfo);
//        log.error("API: excepiton={} cause={} method={} uri={} paramInfo={}", e.getClass().getSimpleName(), e.getMessage(), method, uri, paramInfo);
        log.error("", e);
//        emailService.sendException(e.getMessage() + ",method:" + method + ", uri:" + uri + ", paramInfo:" + paramInfo + ", datetime:" + DateUtil.formatAlternativeIso8601Date(new Date()), e);
        ResultVO resultVO = new ResultVO(ResultStatus.BUSINESS_ERROR.getCode());
        resultVO.setMessage(e.getMessage());
        if ("prod".equals(profiles)) {
            resultVO.setMessage(ResultStatus.BUSINESS_ERROR.getTips());
        }
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(resultVO);
    }


    /**
     * 业务异常处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler({VideoLimitException.class})
    public ResponseEntity<Object> handleVideoLimitException(HttpServletRequest request,VideoLimitException e) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paramInfo = "";
        paramInfo = getParam(request, method, paramInfo);
        log.error("", e);
        ResultVO resultVO = new ResultVO(ResultStatus.VIDEO_LIMIT_ERROR.getCode());
        resultVO.setMessage(e.getMessage());
        if ("prod".equals(profiles)) {
            resultVO.setMessage(ResultStatus.VIDEO_LIMIT_ERROR.getTips());
        }
        return ResponseEntity.status(HttpStatus.OK).body(resultVO);
    }

    /**
     * 平台基类异常处理
     *
     * @param e
     * @param request request
     * @param response response
     * @return
     */
    @ExceptionHandler({PlatformException.class})
    public ResponseEntity<Object> handlePlatformException(PlatformException e, HttpServletRequest request, HttpServletResponse response) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paramInfo = "";
        paramInfo = getParam(request, method, paramInfo);
        log.error("API: excepiton={} cause={} method={} uri={} paramInfo={}", e.getClass().getSimpleName(), e.getMessage(), method, uri, paramInfo);
        log.error("", e);
        formatException(e, method, uri, paramInfo);

        ResultVO resultVO = new ResultVO(ResultStatus.FAILED.getCode());
        resultVO.setMessage(e.getMessage());
        if ("prod".equals(profiles)) {
            resultVO.setMessage(ResultStatus.FAILED.getTips());
        }
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resultVO);
    }

    /**
     * 权限异常处理
     *
     * @param exception exception
     * @return ResponseEntity
     */
    @ExceptionHandler({AccessDeniedException.class})
    public ResponseEntity<Object> handleAccessDeniedException(AccessDeniedException exception) {
        logger.error("【handleAccessDeniedException】：" + exception.getMessage());
        ResultVO resultVO = new ResultVO(ResultStatus.NO_AUTH.getCode());
        resultVO.setMessage(exception.getMessage());
        if ("prod".equals(profiles)) {
            resultVO.setMessage(ResultStatus.NO_AUTH.getTips());
        }
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(resultVO);
    }

    /**
     * 业务异常处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler({Exception.class})
    public ResponseEntity<Object> handleBusinessException(HttpServletRequest request, Exception e) {
        commonTaskExecutor.execute(() -> apiRequestStatisticsService.recordError(request));
        ResultVO resultVO = new ResultVO(ResultStatus.FAILED.getCode());
        resultVO.setMessage(e.getMessage());
        if ("prod".equals(profiles)) {
            resultVO.setMessage(ResultStatus.FAILED.getTips());
        }
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paramInfo = "";
        paramInfo = getParam(request, method, paramInfo);
        log.error("API: excepiton={} cause={} method={} uri={} paramInfo={}", e.getClass().getSimpleName(), e.getMessage(), method, uri, paramInfo);
        log.error("", e);
        formatException(e, method, uri, paramInfo);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resultVO);
    }

    private String getParam(HttpServletRequest request, String method, String paramInfo) {
        if ("GET".equals(method)) {
            paramInfo = request.getQueryString();
        } else if (("POST".equals(method) || "PUT".equals(method))) {
            String contentType = request.getHeader("Content-Type");
            if (contentType != null && !contentType.contains("multipart")) {
                Map<String, String[]> params = request.getParameterMap();
                if (params != null && params.size() == 0) {
                    paramInfo = HttpHelper.getBodyString(request);
                } else {
                    Map<String, Object> paramsObj = new HashMap<>();
                    for (String key : params.keySet()) {
                        paramsObj.put(key, params.get(key));
                    }
                    try {
                        paramInfo = objectMapper.writeValueAsString(paramsObj);
                    } catch (JsonProcessingException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return paramInfo;
    }

    private void formatException(Exception e, String method, String uri, String paramInfo) {
        sendExceptionUtil.sendException("\n4. [" + method + "]：" + uri + "\n5. [paramInfo]：" + paramInfo + "\n6. [datetime]：" + DateUtil.getStringDate(), e);
    }


}
